
/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 *版权所有2002-2018原创作者。
 *
 *根据Apache许可证2.0版许可（“许可证”）；
 *除非符合许可证的规定，否则您不得使用此文件。
 *您可以在以下网址获取许可证副本：
 *
 *https://www.apache.org/licenses/LICENSE-2.0
 *
 *除非适用法律要求或书面同意，软件
 *根据许可证进行的分发是在“按原样”的基础上进行的，
 *无任何明示或暗示的保证或条件。
 *有关管理权限的特定语言，请参阅许可证和
 *许可证下的限制。
 */

package org.springframework.jmx.export.assembler;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import javax.management.Descriptor;
import javax.management.JMException;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.modelmbean.ModelMBeanAttributeInfo;
import javax.management.modelmbean.ModelMBeanOperationInfo;

import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.jmx.support.JmxUtils;
import org.springframework.lang.Nullable;

/**
 * Builds on the {@link AbstractMBeanInfoAssembler} superclass to
 * add a basic algorithm for building metadata based on the
 * reflective metadata of the MBean class.
 *
 * <p>The logic for creating MBean metadata from the reflective metadata
 * is contained in this class, but this class makes no decisions as to
 * which methods and properties are to be exposed. Instead, it gives
 * subclasses a chance to 'vote' on each property or method through
 * the {@code includeXXX} methods.
 *
 * <p>Subclasses are also given the opportunity to populate attribute
 * and operation metadata with additional descriptors once the metadata
 * is assembled through the {@code populateXXXDescriptor} methods.
 *
 * @author Rob Harrop
 * @author Juergen Hoeller
 * @author David Boden
 * @since 1.2
 * @see #includeOperation
 * @see #includeReadAttribute
 * @see #includeWriteAttribute
 * @see #populateAttributeDescriptor
 * @see #populateOperationDescriptor
 */
/**
 *基于｛@link AbstractMBeanInfoAssembler｝超类构建
 *添加了一个基于
 *MBean类的反射元数据。
 *
 *<p>从反射元数据创建MBean元数据的逻辑
 *包含在该类中，但该类不决定
 *哪些方法和特性将被暴露。相反，它给出了
 *通过子类化对每个属性或方法进行“投票”的机会
 *{@code-includeXX}方法。
 *
 *＜p＞子类也有机会填充属性
 *以及操作元数据和附加描述符
 *是通过｛@code populateXXDescriptor｝方法组装的。
 *
 *@作者Rob Harrop
 *@作者于尔根·霍勒
 *@作者David Boden
 *@自1.2起
 *@参见#includeOperation
 *@参见#includeReadAttribute
 *@参见#includeWriteAttribute
 *@参见#populateAttributeDescriptor
 *@参见#populateOperationDescriptor
 */
public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBeanInfoAssembler {

	/**
	 * Identifies a getter method in a JMX {@link Descriptor}.
	 */
	/**
	 *标识JMX｛@link Descriptor｝中的getter方法。
	 */
	protected static final String FIELD_GET_METHOD = "getMethod";

	/**
	 * Identifies a setter method in a JMX {@link Descriptor}.
	 */
	/**
	 *标识JMX｛@link Descriptor｝中的setter方法。
	 */
	protected static final String FIELD_SET_METHOD = "setMethod";

	/**
	 * Constant identifier for the role field in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中角色字段的常量标识符。
	 */
	protected static final String FIELD_ROLE = "role";

	/**
	 * Constant identifier for the getter role field value in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中getter角色字段值的常量标识符。
	 */
	protected static final String ROLE_GETTER = "getter";

	/**
	 * Constant identifier for the setter role field value in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中setter角色字段值的常量标识符。
	 */
	protected static final String ROLE_SETTER = "setter";

	/**
	 * Identifies an operation (method) in a JMX {@link Descriptor}.
	 */
	/**
	 *标识JMX｛@link Descriptor｝中的操作（方法）。
	 */
	protected static final String ROLE_OPERATION = "operation";

	/**
	 * Constant identifier for the visibility field in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中可见性字段的常量标识符。
	 */
	protected static final String FIELD_VISIBILITY = "visibility";

	/**
	 * Lowest visibility, used for operations that correspond to
	 * accessors or mutators for attributes.
	 * @see #FIELD_VISIBILITY
	 */
	/**
	 *最低可见性，用于与
	 *属性的访问器或赋值器。
	 *@参见#FIELD_VISIBILITY
	 */
	protected static final int ATTRIBUTE_OPERATION_VISIBILITY = 4;

	/**
	 * Constant identifier for the class field in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中类字段的常量标识符。
	 */
	protected static final String FIELD_CLASS = "class";
	/**
	 * Constant identifier for the log field in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中日志字段的常量标识符。
	 */
	protected static final String FIELD_LOG = "log";

	/**
	 * Constant identifier for the logfile field in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中日志文件字段的常量标识符。
	 */
	protected static final String FIELD_LOG_FILE = "logFile";

	/**
	 * Constant identifier for the currency time limit field in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中货币时限字段的常量标识符。
	 */
	protected static final String FIELD_CURRENCY_TIME_LIMIT = "currencyTimeLimit";

	/**
	 * Constant identifier for the default field in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中默认字段的常量标识符。
	 */
	protected static final String FIELD_DEFAULT = "default";

	/**
	 * Constant identifier for the persistPolicy field in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中persistPolicy字段的常量标识符。
	 */
	protected static final String FIELD_PERSIST_POLICY = "persistPolicy";

	/**
	 * Constant identifier for the persistPeriod field in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中persistPeriod字段的常量标识符。
	 */
	protected static final String FIELD_PERSIST_PERIOD = "persistPeriod";

	/**
	 * Constant identifier for the persistLocation field in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中persistLocation字段的常量标识符。
	 */
	protected static final String FIELD_PERSIST_LOCATION = "persistLocation";

	/**
	 * Constant identifier for the persistName field in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中persistName字段的常量标识符。
	 */
	protected static final String FIELD_PERSIST_NAME = "persistName";

	/**
	 * Constant identifier for the displayName field in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中displayName字段的常量标识符。
	 */
	protected static final String FIELD_DISPLAY_NAME = "displayName";

	/**
	 * Constant identifier for the units field in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中单位字段的常量标识符。
	 */
	protected static final String FIELD_UNITS = "units";

	/**
	 * Constant identifier for the metricType field in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中metricType字段的常量标识符。
	 */
	protected static final String FIELD_METRIC_TYPE = "metricType";

	/**
	 * Constant identifier for the custom metricCategory field in a JMX {@link Descriptor}.
	 */
	/**
	 *JMX｛@link Descriptor｝中自定义metricCategory字段的常量标识符。
	 */
	protected static final String FIELD_METRIC_CATEGORY = "metricCategory";


	/**
	 * Default value for the JMX field "currencyTimeLimit".
	 */
	/**
	 *JMX字段“currencyTimeLimit”的默认值。
	 */
	@Nullable
	private Integer defaultCurrencyTimeLimit;

	/**
	 * Indicates whether strict casing is being used for attributes.
	 */
	/**
	 *指示是否对属性使用严格大小写。
	 */
	private boolean useStrictCasing = true;

	private boolean exposeClassDescriptor = false;

	@Nullable
	private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();


	/**
	 * Set the default for the JMX field "currencyTimeLimit".
	 * The default will usually indicate to never cache attribute values.
	 * <p>Default is none, not explicitly setting that field, as recommended by the
	 * JMX 1.2 specification. This should result in "never cache" behavior, always
	 * reading attribute values freshly (which corresponds to a "currencyTimeLimit"
	 * of {@code -1} in JMX 1.2).
	 * <p>However, some JMX implementations (that do not follow the JMX 1.2 spec
	 * in that respect) might require an explicit value to be set here to get
	 * "never cache" behavior: for example, JBoss 3.2.x.
	 * <p>Note that the "currencyTimeLimit" value can also be specified on a
	 * managed attribute or operation. The default value will apply if not
	 * overridden with a "currencyTimeLimit" value {@code >= 0} there:
	 * a metadata "currencyTimeLimit" value of {@code -1} indicates
	 * to use the default; a value of {@code 0} indicates to "always cache"
	 * and will be translated to {@code Integer.MAX_VALUE}; a positive
	 * value indicates the number of cache seconds.
	 * @see org.springframework.jmx.export.metadata.AbstractJmxAttribute#setCurrencyTimeLimit
	 * @see #applyCurrencyTimeLimit(javax.management.Descriptor, int)
	 */
	/**
	 *设置JMX字段“currencyTimeLimit”的默认值。
	 *默认值通常表示从不缓存属性值。
	 *＜p＞默认值为none，而不是按照
	 *JMX 1.2规范。这应该导致“从不缓存”的行为，总是
	 *新读取属性值（对应于“currencyTimeLimit”
	 *JMX 1.2中｛@code-1｝的）。
	 *<p>然而，一些JMX实现（不遵循JMX 1.2规范
	 *在这方面）可能需要在此处设置显式值以获得
	 *“从不缓存”行为：例如，JBoss 3.2.x。
	 *<p>请注意，“currencyTimeLimit”值也可以在
	 *托管属性或操作。如果没有，则应用默认值
	 *用“currencyTimeLimit”值｛@code>=0｝重写：
	 *｛@code-1｝的元数据“currencyTimeLimit”值表示
	 *使用默认值；｛@code 0｝的值表示“始终缓存”
	 *并将被转换为{@code Integer.MAX_VALUE}；积极的
	 *值表示缓存秒数。
	 *@参见org.springframework.jmx.export.metadata.AbstractJmxAttribute#setCurrencyTimeLimit
	 *@见#applyCurrentTimeLimit（javax.management.Descriptionr，int）
	 */
	public void setDefaultCurrencyTimeLimit(@Nullable Integer defaultCurrencyTimeLimit) {
		this.defaultCurrencyTimeLimit = defaultCurrencyTimeLimit;
	}

	/**
	 * Return default value for the JMX field "currencyTimeLimit", if any.
	 */
	/**
	 *返回JMX字段“currencyTimeLimit”的默认值（如果有的话）。
	 */
	@Nullable
	protected Integer getDefaultCurrencyTimeLimit() {
		return this.defaultCurrencyTimeLimit;
	}

	/**
	 * Set whether to use strict casing for attributes. Enabled by default.
	 * <p>When using strict casing, a JavaBean property with a getter such as
	 * {@code getFoo()} translates to an attribute called {@code Foo}.
	 * With strict casing disabled, {@code getFoo()} would translate to just
	 * {@code foo}.
	 */
	/**
	 *设置是否对属性使用严格大小写。默认情况下启用。
	 *<p>当使用严格的大小写时，带有getter的JavaBean属性
	 *｛@codegetFoo（）｝转换为一个名为｛@codeFoo｝的属性。
	 *在禁用严格大小写的情况下，{@codegetFoo（）}将转换为
	 *｛@code foo｝。
	 */
	public void setUseStrictCasing(boolean useStrictCasing) {
		this.useStrictCasing = useStrictCasing;
	}

	/**
	 * Return whether strict casing for attributes is enabled.
	 */
	/**
	 *返回是否启用了属性的严格大小写。
	 */
	protected boolean isUseStrictCasing() {
		return this.useStrictCasing;
	}

	/**
	 * Set whether to expose the JMX descriptor field "class" for managed operations.
	 * Default is "false", letting the JMX implementation determine the actual class
	 * through reflection.
	 * <p>Set this property to {@code true} for JMX implementations that
	 * require the "class" field to be specified, for example WebLogic's.
	 * In that case, Spring will expose the target class name there, in case of
	 * a plain bean instance or a CGLIB proxy. When encountering a JDK dynamic
	 * proxy, the <b>first</b> interface implemented by the proxy will be specified.
	 * <p><b>WARNING:</b> Review your proxy definitions when exposing a JDK dynamic
	 * proxy through JMX, in particular with this property turned to {@code true}:
	 * the specified interface list should start with your management interface in
	 * this case, with all other interfaces following. In general, consider exposing
	 * your target bean directly or a CGLIB proxy for it instead.
	 * @see #getClassForDescriptor(Object)
	 */
	/**
	 *设置是否为托管操作公开JMX描述符字段“class”。
	 *默认值为“false”，让JMX实现确定实际的类
	 *通过反射。
	 *＜p＞对于以下JMX实现，将此属性设置为｛@code true｝
	 *要求指定“类”字段，例如WebLogic。
	 *在这种情况下，Spring将在那里公开目标类名，以防
	 *普通bean实例或CGLIB代理。遇到JDK动态时
	 *代理，将指定代理实现的<b>第一个</b>接口。
	 *<p><b>警告：</b>在公开JDK动态时请查看代理定义
	 *通过JMX代理，特别是将此属性转换为{@code-true}：
	 *指定的接口列表应该以中的管理接口开始
	 *在这种情况下，所有其他接口都在后面。一般来说，考虑暴露
	 *您的目标bean，或者代之以CGLIB代理。
	 *@参见#getClassForDescriptor（对象）
	 */
	public void setExposeClassDescriptor(boolean exposeClassDescriptor) {
		this.exposeClassDescriptor = exposeClassDescriptor;
	}

	/**
	 * Return whether to expose the JMX descriptor field "class" for managed operations.
	 */
	/**
	 *返回是否为托管操作公开JMX描述符字段“class”。
	 */
	protected boolean isExposeClassDescriptor() {
		return this.exposeClassDescriptor;
	}

	/**
	 * Set the ParameterNameDiscoverer to use for resolving method parameter
	 * names if needed (e.g. for parameter names of MBean operation methods).
	 * <p>Default is a {@link DefaultParameterNameDiscoverer}.
	 */
	/**
	 *设置用于解析方法参数的ParameterNameDiscoverer
	 *名称（例如用于MBean操作方法的参数名称）。
	 *＜p＞默认值为｛@link DefaultParameterNameDiscoverer｝。
	 */
	public void setParameterNameDiscoverer(@Nullable ParameterNameDiscoverer parameterNameDiscoverer) {
		this.parameterNameDiscoverer = parameterNameDiscoverer;
	}

	/**
	 * Return the ParameterNameDiscoverer to use for resolving method parameter
	 * names if needed (may be {@code null} in order to skip parameter detection).
	 */
	/**
	 *返回用于解析方法参数的ParameterNameDiscoverer
	 *名称（可以是{@code-null}以便跳过参数检测）。
	 */
	@Nullable
	protected ParameterNameDiscoverer getParameterNameDiscoverer() {
		return this.parameterNameDiscoverer;
	}


	/**
	 * Iterate through all properties on the MBean class and gives subclasses
	 * the chance to vote on the inclusion of both the accessor and mutator.
	 * If a particular accessor or mutator is voted for inclusion, the appropriate
	 * metadata is assembled and passed to the subclass for descriptor population.
	 * @param managedBean the bean instance (might be an AOP proxy)
	 * @param beanKey the key associated with the MBean in the beans map
	 * of the {@code MBeanExporter}
	 * @return the attribute metadata
	 * @throws JMException in case of errors
	 * @see #populateAttributeDescriptor
	 */
	/**
	 *遍历MBean类上的所有属性并给出子类
	 *投票决定是否包含访问器和变异器的机会。
	 *如果某个特定的访问器或变异器被投票支持包含，则适当的
	 *元数据被组装并传递给用于描述符填充的子类。
	 *@param managedBean bean实例（可能是AOP代理）
	 *@param beanKey与beans映射中的MBean关联的密钥
	 *｛@code MBeanExporter｝的
	 *@返回属性元数据
	 *出现错误时@throws JMException
	 *@参见#populateAttributeDescriptor
	 */
	@Override
	protected ModelMBeanAttributeInfo[] getAttributeInfo(Object managedBean, String beanKey) throws JMException {
		PropertyDescriptor[] props = BeanUtils.getPropertyDescriptors(getClassToExpose(managedBean));
		List<ModelMBeanAttributeInfo> infos = new ArrayList<>();

		for (PropertyDescriptor prop : props) {
			Method getter = prop.getReadMethod();
			if (getter != null && getter.getDeclaringClass() == Object.class) {
				continue;
			}
			if (getter != null && !includeReadAttribute(getter, beanKey)) {
				getter = null;
			}

			Method setter = prop.getWriteMethod();
			if (setter != null && !includeWriteAttribute(setter, beanKey)) {
				setter = null;
			}

			if (getter != null || setter != null) {
				// If both getter and setter are null, then this does not need exposing.
				// 如果getter和setter都为null，那么这就不需要公开。
				String attrName = JmxUtils.getAttributeName(prop, isUseStrictCasing());
				String description = getAttributeDescription(prop, beanKey);
				ModelMBeanAttributeInfo info = new ModelMBeanAttributeInfo(attrName, description, getter, setter);

				Descriptor desc = info.getDescriptor();
				if (getter != null) {
					desc.setField(FIELD_GET_METHOD, getter.getName());
				}
				if (setter != null) {
					desc.setField(FIELD_SET_METHOD, setter.getName());
				}

				populateAttributeDescriptor(desc, getter, setter, beanKey);
				info.setDescriptor(desc);
				infos.add(info);
			}
		}

		return infos.toArray(new ModelMBeanAttributeInfo[0]);
	}

	/**
	 * Iterate through all methods on the MBean class and gives subclasses the chance
	 * to vote on their inclusion. If a particular method corresponds to the accessor
	 * or mutator of an attribute that is included in the management interface, then
	 * the corresponding operation is exposed with the &quot;role&quot; descriptor
	 * field set to the appropriate value.
	 * @param managedBean the bean instance (might be an AOP proxy)
	 * @param beanKey the key associated with the MBean in the beans map
	 * of the {@code MBeanExporter}
	 * @return the operation metadata
	 * @see #populateOperationDescriptor
	 */
	/**
	 *遍历MBean类上的所有方法，并为子类提供机会
	 *对他们的加入进行投票。如果特定方法对应于访问者
	 *或管理接口中包含的属性的变异器，然后
	 *相应的操作用“；角色”；描述符
	 *字段设置为适当的值。
	 *@param managedBean bean实例（可能是AOP代理）
	 *@param beanKey与beans映射中的MBean关联的密钥
	 *｛@code MBeanExporter｝的
	 *@返回操作元数据
	 *@参见#populateOperationDescriptor
	 */
	@Override
	protected ModelMBeanOperationInfo[] getOperationInfo(Object managedBean, String beanKey) {
		Method[] methods = getClassToExpose(managedBean).getMethods();
		List<ModelMBeanOperationInfo> infos = new ArrayList<>();

		for (Method method : methods) {
			if (method.isSynthetic()) {
				continue;
			}
			if (Object.class == method.getDeclaringClass()) {
				continue;
			}

			ModelMBeanOperationInfo info = null;
			PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
			if (pd != null && ((method.equals(pd.getReadMethod()) && includeReadAttribute(method, beanKey)) ||
						(method.equals(pd.getWriteMethod()) && includeWriteAttribute(method, beanKey)))) {
				// Attributes need to have their methods exposed as
				// 属性需要将其方法公开为
				// operations to the JMX server as well.
				// 操作到JMX服务器。
				info = createModelMBeanOperationInfo(method, pd.getName(), beanKey);
				Descriptor desc = info.getDescriptor();
				if (method.equals(pd.getReadMethod())) {
					desc.setField(FIELD_ROLE, ROLE_GETTER);
				}
				else {
					desc.setField(FIELD_ROLE, ROLE_SETTER);
				}
				desc.setField(FIELD_VISIBILITY, ATTRIBUTE_OPERATION_VISIBILITY);
				if (isExposeClassDescriptor()) {
					desc.setField(FIELD_CLASS, getClassForDescriptor(managedBean).getName());
				}
				info.setDescriptor(desc);
			}

			// allow getters and setters to be marked as operations directly
			// 允许getter和setter直接标记为操作
			if (info == null && includeOperation(method, beanKey)) {
				info = createModelMBeanOperationInfo(method, method.getName(), beanKey);
				Descriptor desc = info.getDescriptor();
				desc.setField(FIELD_ROLE, ROLE_OPERATION);
				if (isExposeClassDescriptor()) {
					desc.setField(FIELD_CLASS, getClassForDescriptor(managedBean).getName());
				}
				populateOperationDescriptor(desc, method, beanKey);
				info.setDescriptor(desc);
			}

			if (info != null) {
				infos.add(info);
			}
		}

		return infos.toArray(new ModelMBeanOperationInfo[0]);
	}

	/**
	 * Creates an instance of {@code ModelMBeanOperationInfo} for the
	 * given method. Populates the parameter info for the operation.
	 * @param method the {@code Method} to create a {@code ModelMBeanOperationInfo} for
	 * @param name the logical name for the operation (method name or property name);
	 * not used by the default implementation but possibly by subclasses
	 * @param beanKey the key associated with the MBean in the beans map
	 * of the {@code MBeanExporter}
	 * @return the {@code ModelMBeanOperationInfo}
	 */
	/**
	 *为创建｛@code ModelMBeanOperationInfo｝的实例
	 *给定的方法。填充操作的参数信息。
	 *@param方法为创建｛@code ModelMBeanOperationInfo｝的｛@codeMethod｝
	 *@param name操作的逻辑名称（方法名或属性名）；
	 *不被默认实现使用，但可能被子类使用
	 *@param beanKey与beans映射中的MBean关联的密钥
	 *｛@code MBeanExporter｝的
	 *@返回｛@code ModelMBeanOperationInfo｝
	 */
	protected ModelMBeanOperationInfo createModelMBeanOperationInfo(Method method, String name, String beanKey) {
		MBeanParameterInfo[] params = getOperationParameters(method, beanKey);
		if (params.length == 0) {
			return new ModelMBeanOperationInfo(getOperationDescription(method, beanKey), method);
		}
		else {
			return new ModelMBeanOperationInfo(method.getName(),
				getOperationDescription(method, beanKey),
				getOperationParameters(method, beanKey),
				method.getReturnType().getName(),
				MBeanOperationInfo.UNKNOWN);
		}
	}

	/**
	 * Return the class to be used for the JMX descriptor field "class".
	 * Only applied when the "exposeClassDescriptor" property is "true".
	 * <p>The default implementation returns the first implemented interface
	 * for a JDK proxy, and the target class else.
	 * @param managedBean the bean instance (might be an AOP proxy)
	 * @return the class to expose in the descriptor field "class"
	 * @see #setExposeClassDescriptor
	 * @see #getClassToExpose(Class)
	 * @see org.springframework.aop.framework.AopProxyUtils#proxiedUserInterfaces(Object)
	 */
	/**
	 *返回要用于JMX描述符字段“class”的类。
	 *仅在“exposeClassDescriptor”属性为“true”时应用。
	 *＜p＞默认实现返回第一个实现的接口
	 *对于JDK代理，以及目标类else。
	 *@param managedBean bean实例（可能是AOP代理）
	 *@返回要在描述符字段“class”中公开的类
	 *@参见#setExposeClassDescriptor
	 *@参见#getClassToExpose（类）
	 *@参见org.springframework.aop.framework.AopProxyUtils#proxyUserInterfaces（对象）
	 */
	protected Class<?> getClassForDescriptor(Object managedBean) {
		if (AopUtils.isJdkDynamicProxy(managedBean)) {
			return AopProxyUtils.proxiedUserInterfaces(managedBean)[0];
		}
		return getClassToExpose(managedBean);
	}


	/**
	 * Allows subclasses to vote on the inclusion of a particular attribute accessor.
	 * @param method the accessor {@code Method}
	 * @param beanKey the key associated with the MBean in the beans map
	 * of the {@code MBeanExporter}
	 * @return {@code true} if the accessor should be included in the management interface,
	 * otherwise {@code false}
	 */
	/**
	 *允许子类对包含特定属性访问器进行投票。
	 *@param method访问器｛@code method｝
	 *@param beanKey与beans映射中的MBean关联的密钥
	 *｛@code MBeanExporter｝的
	 *@return｛@code true｝如果访问器应该包含在管理接口中，
	 *否则｛@code false｝
	 */
	protected abstract boolean includeReadAttribute(Method method, String beanKey);

	/**
	 * Allows subclasses to vote on the inclusion of a particular attribute mutator.
	 * @param method the mutator {@code Method}.
	 * @param beanKey the key associated with the MBean in the beans map
	 * of the {@code MBeanExporter}
	 * @return {@code true} if the mutator should be included in the management interface,
	 * otherwise {@code false}
	 */
	/**
	 *允许子类对包含特定属性赋值函数进行投票。
	 *@param method赋值函数｛@code method｝。
	 *@param beanKey与beans映射中的MBean关联的密钥
	 *｛@code MBeanExporter｝的
	 *@return｛@code true｝如果管理接口中应该包含赋值函数，
	 *否则｛@code false｝
	 */
	protected abstract boolean includeWriteAttribute(Method method, String beanKey);

	/**
	 * Allows subclasses to vote on the inclusion of a particular operation.
	 * @param method the operation method
	 * @param beanKey the key associated with the MBean in the beans map
	 * of the {@code MBeanExporter}
	 * @return whether the operation should be included in the management interface
	 */
	/**
	 *允许子类对包含特定操作进行投票。
	 *@param method操作方法
	 *@param beanKey与beans映射中的MBean关联的密钥
	 *｛@code MBeanExporter｝的
	 *@return操作是否应包含在管理界面中
	 */
	protected abstract boolean includeOperation(Method method, String beanKey);

	/**
	 * Get the description for a particular attribute.
	 * <p>The default implementation returns a description for the operation
	 * that is the name of corresponding {@code Method}.
	 * @param propertyDescriptor the PropertyDescriptor for the attribute
	 * @param beanKey the key associated with the MBean in the beans map
	 * of the {@code MBeanExporter}
	 * @return the description for the attribute
	 */
	/**
	 *获取特定属性的描述。
	 *＜p＞默认实现返回操作的描述
	 *即对应的{@code方法}的名称。
	 *@param propertyDescriptor属性的propertyDescriptor
	 *@param beanKey与beans映射中的MBean关联的密钥
	 *｛@code MBeanExporter｝的
	 *@返回属性的描述
	 */
	protected String getAttributeDescription(PropertyDescriptor propertyDescriptor, String beanKey) {
		return propertyDescriptor.getDisplayName();
	}

	/**
	 * Get the description for a particular operation.
	 * <p>The default implementation returns a description for the operation
	 * that is the name of corresponding {@code Method}.
	 * @param method the operation method
	 * @param beanKey the key associated with the MBean in the beans map
	 * of the {@code MBeanExporter}
	 * @return the description for the operation
	 */
	/**
	 *获取特定操作的说明。
	 *＜p＞默认实现返回操作的描述
	 *即对应的{@code方法}的名称。
	 *@param method操作方法
	 *@param beanKey与beans映射中的MBean关联的密钥
	 *｛@code MBeanExporter｝的
	 *@返回操作说明
	 */
	protected String getOperationDescription(Method method, String beanKey) {
		return method.getName();
	}

	/**
	 * Create parameter info for the given method.
	 * <p>The default implementation returns an empty array of {@code MBeanParameterInfo}.
	 * @param method the {@code Method} to get the parameter information for
	 * @param beanKey the key associated with the MBean in the beans map
	 * of the {@code MBeanExporter}
	 * @return the {@code MBeanParameterInfo} array
	 */
	/**
	 *为给定方法创建参数信息。
	 *＜p＞默认实现返回｛@code MBeanParameterInfo｝的空数组。
	 *@param method用于获取的参数信息的｛@code method｝
	 *@param beanKey与beans映射中的MBean关联的密钥
	 *｛@code MBeanExporter｝的
	 *@返回｛@code MBeanParameterInfo｝数组
	 */
	protected MBeanParameterInfo[] getOperationParameters(Method method, String beanKey) {
		ParameterNameDiscoverer paramNameDiscoverer = getParameterNameDiscoverer();
		String[] paramNames = (paramNameDiscoverer != null ? paramNameDiscoverer.getParameterNames(method) : null);
		if (paramNames == null) {
			return new MBeanParameterInfo[0];
		}

		MBeanParameterInfo[] info = new MBeanParameterInfo[paramNames.length];
		Class<?>[] typeParameters = method.getParameterTypes();
		for (int i = 0; i < info.length; i++) {
			info[i] = new MBeanParameterInfo(paramNames[i], typeParameters[i].getName(), paramNames[i]);
		}

		return info;
	}

	/**
	 * Allows subclasses to add extra fields to the {@code Descriptor} for an MBean.
	 * <p>The default implementation sets the {@code currencyTimeLimit} field to
	 * the specified "defaultCurrencyTimeLimit", if any (by default none).
	 * @param descriptor the {@code Descriptor} for the MBean resource.
	 * @param managedBean the bean instance (might be an AOP proxy)
	 * @param beanKey the key associated with the MBean in the beans map
	 * of the {@code MBeanExporter}
	 * @see #setDefaultCurrencyTimeLimit(Integer)
	 * @see #applyDefaultCurrencyTimeLimit(javax.management.Descriptor)
	 */
	/**
	 *允许子类向MBean的｛@code Descriptor｝添加额外字段。
	 *＜p＞默认实现将｛@code currencyTimeLimit｝字段设置为
	 *指定的“defaultCurrencyTimeLimit”（如果有）（默认为无）。
	 *@param descriptor MBean资源的｛@code descriptor｝。
	 *@param managedBean bean实例（可能是AOP代理）
	 *@param beanKey与beans映射中的MBean关联的密钥
	 *｛@code MBeanExporter｝的
	 *@参见#setDefaultCurrencyTimeLimit（整数）
	 *@参见#applyDefaultCurrencyTimeLimit（javax.management.DDescriptor）
	 */
	@Override
	protected void populateMBeanDescriptor(Descriptor descriptor, Object managedBean, String beanKey) {
		applyDefaultCurrencyTimeLimit(descriptor);
	}

	/**
	 * Allows subclasses to add extra fields to the {@code Descriptor} for a
	 * particular attribute.
	 * <p>The default implementation sets the {@code currencyTimeLimit} field to
	 * the specified "defaultCurrencyTimeLimit", if any (by default none).
	 * @param desc the attribute descriptor
	 * @param getter the accessor method for the attribute
	 * @param setter the mutator method for the attribute
	 * @param beanKey the key associated with the MBean in the beans map
	 * of the {@code MBeanExporter}
	 * @see #setDefaultCurrencyTimeLimit(Integer)
	 * @see #applyDefaultCurrencyTimeLimit(javax.management.Descriptor)
	 */
	/**
	 *允许子类为
	 *特定属性。
	 *＜p＞默认实现将｛@code currencyTimeLimit｝字段设置为
	 *指定的“defaultCurrencyTimeLimit”（如果有）（默认为无）。
	 *@param desc属性描述符
	 *@param getter属性的访问器方法
	 *@param setter属性的mutator方法
	 *@param beanKey与beans映射中的MBean关联的密钥
	 *｛@code MBeanExporter｝的
	 *@参见#setDefaultCurrencyTimeLimit（整数）
	 *@参见#applyDefaultCurrencyTimeLimit（javax.management.DDescriptor）
	 */
	protected void populateAttributeDescriptor(
			Descriptor desc, @Nullable Method getter, @Nullable Method setter, String beanKey) {

		applyDefaultCurrencyTimeLimit(desc);
	}

	/**
	 * Allows subclasses to add extra fields to the {@code Descriptor} for a
	 * particular operation.
	 * <p>The default implementation sets the {@code currencyTimeLimit} field to
	 * the specified "defaultCurrencyTimeLimit", if any (by default none).
	 * @param desc the operation descriptor
	 * @param method the method corresponding to the operation
	 * @param beanKey the key associated with the MBean in the beans map
	 * of the {@code MBeanExporter}
	 * @see #setDefaultCurrencyTimeLimit(Integer)
	 * @see #applyDefaultCurrencyTimeLimit(javax.management.Descriptor)
	 */
	/**
	 *允许子类为
	 *特定操作。
	 *＜p＞默认实现将｛@code currencyTimeLimit｝字段设置为
	 *指定的“defaultCurrencyTimeLimit”（如果有）（默认为无）。
	 *@param desc操作描述符
	 *@param method与操作相对应的方法
	 *@param beanKey与beans映射中的MBean关联的密钥
	 *｛@code MBeanExporter｝的
	 *@参见#setDefaultCurrencyTimeLimit（整数）
	 *@参见#applyDefaultCurrencyTimeLimit（javax.management.DDescriptor）
	 */
	protected void populateOperationDescriptor(Descriptor desc, Method method, String beanKey) {
		applyDefaultCurrencyTimeLimit(desc);
	}

	/**
	 * Set the {@code currencyTimeLimit} field to the specified
	 * "defaultCurrencyTimeLimit", if any (by default none).
	 * @param desc the JMX attribute or operation descriptor
	 * @see #setDefaultCurrencyTimeLimit(Integer)
	 */
	/**
	 *将｛@code currencyTimeLimit｝字段设置为指定的
	 *“defaultCurrencyTimeLimit”，如果有（默认为无）。
	 *@param desc JMX属性或操作描述符
	 *@参见#setDefaultCurrencyTimeLimit（整数）
	 */
	protected final void applyDefaultCurrencyTimeLimit(Descriptor desc) {
		if (getDefaultCurrencyTimeLimit() != null) {
			desc.setField(FIELD_CURRENCY_TIME_LIMIT, getDefaultCurrencyTimeLimit().toString());
		}
	}

	/**
	 * Apply the given JMX "currencyTimeLimit" value to the given descriptor.
	 * <p>The default implementation sets a value {@code >0} as-is (as number of cache seconds),
	 * turns a value of {@code 0} into {@code Integer.MAX_VALUE} ("always cache")
	 * and sets the "defaultCurrencyTimeLimit" (if any, indicating "never cache") in case of
	 * a value {@code <0}. This follows the recommendation in the JMX 1.2 specification.
	 * @param desc the JMX attribute or operation descriptor
	 * @param currencyTimeLimit the "currencyTimeLimit" value to apply
	 * @see #setDefaultCurrencyTimeLimit(Integer)
	 * @see #applyDefaultCurrencyTimeLimit(javax.management.Descriptor)
	 */
	/**
	 *将给定的JMX“currencyTimeLimit”值应用于给定的描述符。
	 *＜p＞默认实现将值｛@code＞0｝设置为原样（缓存秒数），
	 *将｛@code 0｝的值转换为｛@code Integer.MAX_value｝（“始终缓存”）
	 *并设置“defaultCurrencyTimeLimit”（如果有，表示“从不缓存”）
	 *值｛@code＜0｝。这遵循了JMX 1.2规范中的建议。
	 *@param desc JMX属性或操作描述符
	 *@param currencyTimeLimit要应用的“currencyTimeLimit”值
	 *@参见#setDefaultCurrencyTimeLimit（整数）
	 *@参见#applyDefaultCurrencyTimeLimit（javax.management.DDescriptor）
	 */
	protected void applyCurrencyTimeLimit(Descriptor desc, int currencyTimeLimit) {
		if (currencyTimeLimit > 0) {
			// number of cache seconds
			// 缓存秒数
			desc.setField(FIELD_CURRENCY_TIME_LIMIT, Integer.toString(currencyTimeLimit));
		}
		else if (currencyTimeLimit == 0) {
			// "always cache"
			// “始终缓存”
			desc.setField(FIELD_CURRENCY_TIME_LIMIT, Integer.toString(Integer.MAX_VALUE));
		}
		else {
			// "never cache"
			// “从不缓存”
			applyDefaultCurrencyTimeLimit(desc);
		}
	}

}
